{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 问题：正则表达式匹配中 (.*) 和 (.*?) 匹配区别？\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "面试官想从这道题中考察面试人什么？\n",
    "要知道正则可以匹配不同的字符集合，你还可以指定它匹配的重复次数。这道题主要考的就是贪婪与非贪婪的使用，当然也会由这道题去触及一些在正则中经常使用到的元字符。\n",
    "\n",
    "答案\n",
    "(.*)是贪婪的，可以去匹配尽可能长的字符串。\n",
    "(.*?)是非贪婪的，去匹配尽可能短的字符串。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 拓展\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "贪婪和非贪婪模式。\n",
    "贪婪模式在整个表达式匹配成功的前提下，尽可能多的匹配，而非贪婪模式在整个表达式匹配成功的前提下，尽可能少的匹配。\n",
    "\n",
    "比如：这样一个字符串pre<tag>a</tag><tag>b</tag><tag>c</tag>post，我们使用<tag>.*</tag>，那么匹配结果会是：<tag>a</tag><tag>b</tag><tag>c</tag>\n",
    "\n",
    "如果我们使用非贪婪模式<tag>.*?</tag>，那么匹配到的结果是：<tag>a</tag>。\n",
    "\n",
    "Tips: *，+，?，{n}，{m, n}，{m, }修饰符都是贪婪的，但在修饰符之后添加?将使样式以非贪婪方式进行匹配。\n",
    "\n",
    "回溯现象\n",
    "现在我们大概了解了贪婪和非贪婪模式的区别了，那么具体的匹配是怎么执行的呢？\n",
    "\n",
    "比如，我们要匹配这个字符串：\n",
    "\n",
    "\"abc\"\n",
    "\n",
    "先看看贪婪模式\".*\"是怎么匹配的？\n",
    "\n",
    "步骤\t匹配\t解释\n",
    "步骤一\t匹配\"\t正则中的\"匹配，匹配的索引指向下一个字符\n",
    "步骤二\t匹配\"abc\"\t贪婪模式.*匹配尽可能多的字符，匹配的索引指向结尾\n",
    "步骤三\t匹配失败\t正则引擎尝试匹配\"，但是当前索引已指向字符串的结束位置，所以匹配失败\n",
    "步骤四\t匹配\"abc\t回溯一次，控制权由交给.*，索引指向倒数一个位置\n",
    "步骤五\t匹配\"abc\"\t控制权又回到\"，刚好匹配成功\n",
    "以上的匹配过程只回溯了一次，下面看看非贪婪模式\".*?\"。\n",
    "\n",
    "步骤\t匹配\t解释\n",
    "步骤一\t匹配\"\t正则中的\"匹配，匹配的索引指向下一个字符\n",
    "步骤二\t匹配\"a\t贪婪模式.*?匹配尽可能少的字符，匹配的索引指向a\n",
    "步骤三\t匹配\"ab\t正则引擎尝试匹配\"，但是当前索引已指向字符b，匹配失败\n",
    "步骤四\t匹配\"abc\t回溯一次，索引再吃进一个字符，当前索引已指向字符c，匹配失败\n",
    "步骤五\t匹配\"abc\"\t回溯一次，索引再吃进一个字符，当前索引已指向字符”，匹配成功\n",
    "贪婪和非贪婪的模式区别：贪婪是先吃进，回溯再让出，非贪婪是先忽略，回溯再吃进。所以，在设计正则模式的时候，尽量减少回溯的次数，但是，也并不是说所有情况下非贪婪模式就一定比贪婪模式回溯的次数多，具体情况要具体分析。\n",
    "\n",
    "在正则表达式中，经常看见一些特殊的字符串，比如|，$，\\s。它们可以表示它们的普通含义，也可以匹配一些与众不同的东西，或者通过重复它们或改变它们的含义来影响正则的其他部分。我们把这些特殊的字符串称之为元字符。\n",
    "\n",
    "元字符的使用\n",
    "元字符的完整列表：\n",
    "\n",
    "元字符\t匹配\n",
    ".\t匹配除了换行的任意字符\n",
    "*\t对它前面的正则式匹配0到任意次重复。\n",
    "?\t对它前面的正则式匹配一次或零次。\n",
    "+\t对它前面的正则式匹配1到任意次重复。\n",
    "{m}\tm次重复\n",
    "{m, n}\tm-n次重复\n",
    "[]\t用于指定字符类，用于匹配一组字符。但是如果元字符出现在这个字符类中，比如[abc$]，那么元字符$会失效，它只作为普通字符使用。\n",
    "^\t在行的开头匹配。\n",
    "$\t匹配以某个字符串结尾。\n",
    "()\t组合匹配。我们将每个括号内的正则看做一个组合，这样在匹配结束后，可以通过对捕获组的索引去引用匹配项，具体例子可以参考上面的匹配对象的group方法。\n",
    "|\tA|B 匹配 A，B中任意一个\n",
    "以 ‘\\’ 开头的特殊序列表示通常有用的预定义字符集。\n",
    "\n",
    "\\d：匹配任何十进制数字，等价于[0-9]\n",
    "\\D：匹配任何非十进制数字的字符。\n",
    "\\w：匹配Unicode词语的字符。\n",
    "\\W：匹配非Unicode词语的字符\n",
    "\\s：匹配任何Unicode空白字符。\n",
    "\\S：匹配任何非空白字符。\n",
    "Tips：上面列举的都是常用的预定义字符集，有时候也会直接问你这些字符集的含义，所以要牢记。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "正则表达式的功能非常强大，也是我们身为程序员必须要掌握和了解的技能，上个小节和这个小节中我们列举了两个经常会在面试中碰到的问题，也拓展了一些 re 模块的用法。\n",
    "\n",
    "但是这对于我们掌握 re 来说是远远不够的，这就要同学们课后继续拓展了，大家加油~"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}